package com.mgg.payment.service.account.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

import javax.annotation.Resource;

import com.mgg.common.client.message.MessageClient;
import com.mgg.common.dao.DaoSupport;
import com.mgg.common.dto.CouponDTO;
import com.mgg.common.dto.OrderDTO;
import com.mgg.common.messaging.RegistShopkeeperProcessor;
import com.mgg.common.payload.RegistShopkeeperPayload;
import com.mgg.common.util.DateUtils;
import com.mgg.common.util.UUIDUtils;
import com.mgg.common.vo.AccountVO;
import com.mgg.payment.service.account.AccountService;
import com.mgg.payment.service.order.SalesOrderService;
import com.mgg.payment.vo.AccountDetailVO;
import com.mgg.payment.vo.BankVO;
import com.mgg.payment.vo.WithdrawalsRecordVO;
import com.mgg.redis.lock.RedisLock;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;

@Service("accountService")
public class AccountServiceImpl implements AccountService{

	private Logger log = LoggerFactory.getLogger(getClass());
	
	@Resource(name = "daoSupport")
	private DaoSupport dao;

	@Autowired
    private RegistShopkeeperProcessor registShopkeeperProcessor;
	
	@Autowired
	private RedisLock redisLock;
	
	@Autowired
	private MessageClient messageClient;

	@Autowired
	private SalesOrderService salesOrderService;
	
	@Autowired
    @Qualifier("poolTaskExecutor")
    private Executor executor;
	
	@Autowired
	private StringRedisTemplate redisTemplate;

	@Override
	public AccountVO getAccountByUserId(String userId) {
		AccountVO account = dao.findForObject("AccountMapper.getAccountByUserId", userId);
		return account;
	}

	@Transactional
	@Override
	public void updateAccount(String userId, BigDecimal totalIncome, BigDecimal amount, BigDecimal preAmount, Integer type, String orderId, String remark) {
		String value = Long.toString(System.currentTimeMillis() + 60000);
		redisLock.lock(userId, value);
		
		Map<String,Object> map = new HashMap<>();
		map.put("userId", userId);
		map.put("amount", amount);
		map.put("preAmount", preAmount);
		map.put("totalIncome", totalIncome);
		dao.save("AccountMapper.updateAccount", map);
		insertAccountLog(userId,amount,preAmount,type,orderId,remark);

		redisLock.unlock(userId, value);
	}

	@Override
	public void insertAccountLog(String userId, BigDecimal amount, BigDecimal preAmount, Integer type, String orderId, String remark) {
		Map<String,Object> map = new HashMap<>();
		map.put("userId", userId);
		map.put("amount", amount);
		map.put("preAmount", preAmount);
		map.put("type", type);
		map.put("orderId", orderId);
		map.put("remark", remark);
		dao.save("AccountMapper.insertAccountLog", map);
	}

    @Override
    public Map<String,Object> listAccountAmountDetail(String userId, Pageable page){
        Map<String,Object> result = new HashMap<>();
        if (page.getPageNumber() == 1) {
            result.put("count",dao.findForObject("AccountMapper.countAccountDetail", userId));
        }
        PageHelper.startPage(page.getPageNumber(), page.getPageSize());
        List<AccountDetailVO> list = dao.findForList("AccountMapper.listAccountAmountDetail", userId);
        result.put("list", list);
        return result;
    }

    @Override
    public Map<String,Object> listAccountPreAmountDetail(String userId, Pageable page){
        Map<String,Object> result = new HashMap<>();
        PageHelper.startPage(page.getPageNumber(), page.getPageSize());
        List<AccountDetailVO> list = dao.findForList("AccountMapper.listAccountPreAmountDetail", userId);
        result.put("list", list);
        return result;
    }

    @Override
    public List<BankVO> listBank() {
        List<BankVO> bankList = dao.findForList("AccountMapper.listBank", null);
        return bankList;
    }

    @Transactional
    @Override
    public void insertWithdrawals(String userId, BigDecimal amount, String type, String name, String account, String bank) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("userId", userId);
        paramMap.put("status", 1);
        String transferNo = System.currentTimeMillis() + RandomStringUtils.randomNumeric(3);
//		String transferNo = OrderCodeUtil.generateOrderCode("5", redisTemplate);
        paramMap.put("transferNo", transferNo);
        paramMap.put("amount", amount);
        paramMap.put("name", name);
        paramMap.put("type", type);
        paramMap.put("account", account);
        paramMap.put("create_time", DateUtils.getTime());
        dao.save("AccountMapper.insertWithdrawals", paramMap);
        String remark = "【提现】 提现单号=" + transferNo;
        updateAccount(userId, null, amount.negate(), null, 6, transferNo, remark);
    }

    @Override
    public List<WithdrawalsRecordVO> listWithdrawalsRecord(String userId, Pageable page) {
        PageHelper.startPage(page.getPageNumber(), page.getPageSize());
        List<WithdrawalsRecordVO> list = dao.findForList("AccountMapper.listWithdrawalsRecord", userId);
        return list;
    }

    @Override
    public CouponDTO getCouponByType(String type, String subType) {
        Map<String,Object> queryMap = new HashMap<>();
        queryMap.put("type", type);
        queryMap.put("subType", subType);
        CouponDTO coupon = dao.findForObject("AccountMapper.getCouponByType", queryMap);
        return coupon;
    }

    @Override
    public CouponDTO getCouponById(String couponId) {
        CouponDTO coupon = dao.findForObject("AccountMapper.getCouponById", couponId);
        return coupon;
    }

    @Override
    public void insertUserCoupon(String userId, CouponDTO coupon, Integer num, String starttime) {
        if (coupon == null) {
            return;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime nowDateTime = LocalDateTime.now();
        LocalDateTime startDateTime = LocalDateTime.parse(starttime, df);

        Integer effectDays = coupon.getEffectDays();
        String endtime = null;
        if (effectDays > 0) {
            LocalDateTime endDatetime = startDateTime.plusDays(effectDays).minusSeconds(1);
            endtime = df.format(endDatetime);
        }
        String addtime = df.format(nowDateTime);
        for (int i = 0; i < num; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("usercoupon_id", UUIDUtils.get32UUID());
            map.put("user_id", userId);
            map.put("coupon_id", coupon.getCouponId());
            map.put("addtime", addtime);
            map.put("starttime", starttime);
            map.put("endtime", endtime);
            map.put("status", 0);
            dao.save("AccountMapper.insertUserCoupon", map);
        }
    }

	@Override
	public Integer insertShopOrderRebateRecord(String userId, String orderId, BigDecimal rebateMoney, double rebateRatio, BigDecimal income, String shopCode, String type, String status, String remark) {
		Map<String,Object> map = new HashMap<>();
		map.put("userId", userId);
		map.put("orderId", orderId);
		map.put("rebateMoney", rebateMoney);
		map.put("rebateRatio", rebateRatio);
		map.put("income", income);
		map.put("shopCode", shopCode);
		map.put("status", status);
		map.put("type", type);
		map.put("remark", remark);
		Integer count = dao.save("AccountMapper.insertShopOrderRebateRecord", map);
		return count;
	}

	@Override
	public Integer insertSalaryDetails(String userId, String sourceUser, BigDecimal amount, String type, String status, String orderId, String remark) {
		Map<String,Object> map = new HashMap<>();
		map.put("userId", userId);
		map.put("source_user", sourceUser);
		map.put("amount", amount);
		map.put("type", type);
		map.put("status", status);
		map.put("orderId", orderId);
		map.put("remark", remark);
        Integer count = dao.save("AccountMapper.insertSalaryDetails", map);
        return count;
	}

    @Override
    public Map<String, String> getTransferByNo(String transferNo, String type, String status) {
        Map<String,Object> map = new HashMap<>();
        map.put("transferNo", transferNo);
        map.put("status", status);
        map.put("type", type);
        return (Map<String, String>) dao.findForObject("AccountMapper.getTransferByNo", map);
    }

    @Override
    public void updateTransfer(String transferNo, String orderNo, String auditedtime, String payTime, int status, String remark) {
        Map<String,Object> map = new HashMap<>();
        map.put("transferNo", transferNo);
        map.put("orderNo", orderNo);
        map.put("audi_time", auditedtime);
        map.put("pay_time", payTime);
        map.put("status", status);
        map.put("remark", remark);
        dao.findForObject("AccountMapper.updateTransfer", map);
    }

    /**
     * 返利佣金分配
     */
    @Override
    public void allocateRebateAmount(OrderDTO order) {
        String userId = order.getUserId();
        String orderId = order.getOrderId();
        String shopCode = order.getShopCode();
        if (StringUtils.isEmpty(shopCode)) {
            return;
        }

        //获取三级分利比率,返利金额
        Map<String,Object> rebateMap =  dao.findForObject("AccountMapper.getMggUserRebate", null);
        log.info("返利比率：{}",rebateMap);

        List<Map<String,Object>> goodsRabateInfoList = salesOrderService.listOrderDetailGoodsRebateInfo(orderId);
        BigDecimal rebateAmount = new BigDecimal(0);
        for (Map<String,Object> goodsRabateInfoMap : goodsRabateInfoList) {
            BigDecimal goodsTotal = new BigDecimal(goodsRabateInfoMap.get("goods_total").toString());
            BigDecimal goodsRebate = new BigDecimal(goodsRabateInfoMap.get("rebate").toString());
            rebateAmount = rebateAmount.add(goodsTotal.multiply(goodsRebate));
        }

        if (rebateAmount.compareTo(new BigDecimal(0)) < 1) {
            return;
        }
        ObjectMapper mapper= new ObjectMapper();
        String rebateRemark = null;
        try {
            rebateRemark = mapper.writeValueAsString(goodsRabateInfoList);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        log.info("返利金额：{}",rebateAmount);

        //第一级返利分配
        String firstUserId = dao.findForObject("getUserIdByShopCode",shopCode);
        double firstRebateRatio =  Double.parseDouble(rebateMap.get("firstRebate").toString());
        BigDecimal firstAmount = rebateAmount.multiply(new BigDecimal(firstRebateRatio)).setScale(2, BigDecimal.ROUND_DOWN);
        updateAccount(firstUserId,firstAmount,null,firstAmount,1,orderId,"销售订单：" +orderId);
        insertShopOrderRebateRecord(firstUserId,orderId,rebateAmount,firstRebateRatio,firstAmount,shopCode,"1","100",rebateRemark);
        log.info("第一级返利分配, userId = {}, rebateMoney = {}, rebateRatio = {}",firstUserId,rebateAmount,firstRebateRatio);

        //发送短信
        String amount = rebateAmount.setScale(2, BigDecimal.ROUND_DOWN).toString();
        CompletableFuture.runAsync(() -> { messageClient.sendPayNotice(userId,firstUserId,amount); }, executor);


        // 第二级返利分配
        // 查找上级
        String secondUserId = dao.findForObject("getSuperIdByUserId",firstUserId);
        if (StringUtils.isEmpty(secondUserId)) {
            return;
        }
        double secondRebateRatio = Double.parseDouble(rebateMap.get("secondRebate").toString());
        BigDecimal secondAmount = rebateAmount.multiply(new BigDecimal(secondRebateRatio)).setScale(2, BigDecimal.ROUND_DOWN);
        String secondRemark = "返利金额：" + rebateAmount.doubleValue() + "，返利率：" + secondRebateRatio;
        insertSalaryDetails(secondUserId,userId,secondAmount,"1","100",orderId,secondRemark);
        log.info("第二级返利工资分配, userId = {}, rebateMoney = {}, rebateRatio = {}",secondUserId,rebateAmount,secondRebateRatio);

        // 第三级返利分配
        String thirdUserId = dao.findForObject("getSuperIdByUserId",secondUserId);
        if (StringUtils.isEmpty(thirdUserId)) {
            return;
        }
        double thirdRebateRatio = Double.parseDouble(rebateMap.get("thirdRebate").toString());
        BigDecimal thirdAmount = rebateAmount.multiply(new BigDecimal(thirdRebateRatio)).setScale(2, BigDecimal.ROUND_DOWN);
        String thirdRemark = "返利金额：" + rebateAmount.doubleValue() + "，返利率：" + thirdRebateRatio;
        insertSalaryDetails(thirdUserId,userId,thirdAmount,"1","100",orderId,thirdRemark);
        log.info("第三级返利工资分配, userId = {}, rebateMoney = {}, rebateRatio = {}",thirdUserId,rebateAmount,thirdRebateRatio);
    }

    /**
	 * 处理开店大礼包订单结算业务
	 * @throws Exception 
	 */
	@Override
	public void processesRegistShopkeeperOrder(String userId, OrderDTO order) {
		String shopCode = order.getShopCode();
		String orderId = order.getOrderId();
		if (StringUtils.isEmpty(shopCode)) {
			shopCode = "AD7777";
		}
		
		Integer orderType = null;
		Integer giveNum = 0;
		try {
			Map<String,Integer> openGiftConfig = salesOrderService.getOpenGiftConfigByOrderId(order.getOrderId());
			orderType = openGiftConfig.get("order_type");
			giveNum = openGiftConfig.get("give_num");
			sendRegistShopkeeperOrderRebate(order, userId, orderType, 1);
			String recommendUserId = dao.findForObject("getUserIdByShopCode",shopCode);
			sendRegistShopkeeperOrderRebate(order, recommendUserId, orderType, 2);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("开店大礼包订单处理失败， orderId = {}", orderId);
		}
		
		RegistShopkeeperPayload payload = new RegistShopkeeperPayload();
    	payload.setUserId(userId);
    	payload.setShopCode(shopCode);
    	payload.setRegistTime(order.getPaytime());
    	payload.setOrderType(orderType);
    	payload.setGiveNum(giveNum);
    	Message<RegistShopkeeperPayload> message = MessageBuilder.withPayload(payload).build();
    	registShopkeeperProcessor.output().send(message);
	}

	@Transactional
	@Override
	public void sendRegistShopkeeperOrderRebate(OrderDTO order, String userId, Integer orderType, Integer userType) {
		String orderId = order.getOrderId();
		String shopCode = order.getShopCode();
		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		LocalDateTime startDatetime = LocalDateTime.now();
		
		Map<String,Object> queryMap = new HashMap<>();
		queryMap.put("orderType", orderType);
		queryMap.put("userType", userType);
		Map<String,Object> rebateRuleMap = dao.findForObject("AccountMapper.getUserRebateRule", queryMap);
		if (MapUtils.isEmpty(rebateRuleMap)) {
			return;
		}

		String couponJson = (String) rebateRuleMap.get("coupon");
		if (!StringUtils.isEmpty(couponJson)) {
			ObjectMapper mapper = new ObjectMapper();
            List<Map<String,String>> couponList = null;
            try {
                couponList = mapper.readValue(couponJson,new TypeReference<List<Map<String,String>>>() { });
            } catch (IOException e) {
                e.printStackTrace();
            }
            for (Map<String,String> couponMap : couponList) {
				String couponId = couponMap.get("coupon_id");
				Integer couponNum = Integer.parseInt(couponMap.get("coupon_num"));
				CouponDTO coupon = getCouponById(couponId);
				insertUserCoupon(userId,coupon,couponNum,df.format(startDatetime) + " 00:00:00");
			}
		}
		
		BigDecimal amount = new BigDecimal(rebateRuleMap.get("amount").toString());
		if (amount.compareTo(new BigDecimal(0)) > 0) {
			updateAccount(userId,amount,null,amount,4,orderId,"");	
			insertShopOrderRebateRecord(userId,orderId,order.getTotalPrice(),0,amount,shopCode,"2","100","");
		}
	}

    @Override
    public void updateShopSales(OrderDTO order) {
        String shopCode = order.getShopCode();
        String shopCodes = dao.findForObject("AccountMapper.getUserShopCodes", shopCode);

        if (StringUtils.isEmpty(shopCodes)) {
            shopCodes = shopCode;
        } else {
            if (!shopCodes.contains(shopCode)) {
                shopCodes = shopCodes + "-" + shopCode;
            }
        }
        List<String> shopCodeList = Arrays.asList(shopCodes.split("-"));

        BigDecimal registerAmount = null;
        BigDecimal goodsAmount = null;
        if (5 == order.getOrderType()) {
            registerAmount = order.getTotalPrice();
        } else {
            goodsAmount = order.getTotalPrice();
        }

        Map<String, Object> map = new HashMap<>();
        map.put("registerSale", registerAmount);
        map.put("goodsSale", goodsAmount);
        for (String shop : shopCodeList) {
            map.put("shopCode", shop);
            dao.update("AccountMapper.updateShopSalesCount", map);
        }
    }
}
